<?php

defined('_JEXEC') or die('Restricted access');

/**
 * 
 * 
 */
class formValidator {
    //const REGEX_ALPHA = '/^[A-Z.]+$/i';
    //const REGEX_ALPHA_NUM = '/^[A-Z0-9._+-]+$/i';

    protected $_postedFormFields = array();
    protected $_rules = array();
    protected $_errors = array();

    /**
     * The Form Validator
     * @param array $posted_form_fields
     * @param array $rules 
     */
    public function __construct($posted_form_fields, $rules) {
        if (!is_array($rules)) {
            throw new Exception("[Rules] MUST be an array of rules");
        }
        $this->_postedFormFields = $posted_form_fields;
        $this->_rules = $rules;
    }

    /**
     * Function handles form data validation
     * @return array an array which includes validation errors 
     */
    public function validate() {
        if (!is_array($this->_postedFormFields)) {
            // did you provide an array ?
            throw new Exception("fields to validate must be in an array");
        }

        foreach ($this->_postedFormFields as $field => $value) {

//========================================================================================================            
//here, check for the required rules, these are rules that must be declared in 
// the Rules validation array
            // Required Rule <optional>
            // Usage :
            // 'username' => array(
            //    'optional' => TRUE or FALSE,   // OR
            //    'optional' => array(
            //       'value' => FALSE, //here (in an array) must be boolean
            //       'custom_msg'=>"Custom msg" 
            //    ),
            //  ),
            //
            //
            
            if (!isset($this->_rules[$field]['optional'])) {
                throw new Exception("Validation rule [optional] is required for all submited (posted) form's fields");
            }

            if (is_array($this->_rules[$field]['optional'])) {
                if (!isset($this->_rules[$field]['optional']['value'])) {
                    throw new Exception("[value] is required for Validation rule [optional] and is missing.");
                }
                if (!is_bool($this->_rules[$field]['optional']['value'])) {
                    throw new Exception("[value], for Validation rule [optional], must be Boolean");
                }

                if ($this->_rules[$field]['optional']['value'] === FALSE) {
                    if (empty($value)) {
                        if (isset($this->_rules[$field]['optional']['custom_msg'])) {
                            $this->_errors[$field][] = $this->_rules[$field]['optional']['custom_msg'];
                        } else {
                            $this->_errors[$field][] = 'This field is required. Please enter a value.';
                        }
                    }
                }
            } else if (is_bool($this->_rules[$field]['optional'])) {
                if ($this->_rules[$field]['optional'] === FALSE) {
                    if (empty($value)) {
                        $this->_errors[$field][] = 'This field is required. Please enter a value.';
                    }
                }
            } else {
                throw new Exception("Validation rule [optional] is required for all submited (posted) form's fields and must be Boolean or Array");
            }
            // END of Required Rule <optional>
            // Required Rule <some other rule....>
            // 
            // END of <some other rule....>
//========================================================================================================            
            // Remove 'optional' from list of callable functions.
            // We already did this check above.
            unset($this->_rules[$field]['optional']);

            foreach ($this->_rules[$field] as $rule => $rule_value) {
                if (method_exists($this, $rule) === TRUE) {
                    call_user_func_array(array($this, $rule),
                            // Pass the Field name, Field value, and Rule value
                            //array($field, $this->_postedFormFields[$field], $rule_value));
                            array($field, $value, $rule_value));
                } else {
                    throw new Exception("Requested Validation Rule [$rule] does not exists.");
                }
            }
        }

        // Return validation result
        if (empty($this->_errors)) {
            return TRUE;
        } else {
            return FALSE;
        }
    }

    /**
     * Function returns form data
     * 
     * @return array form fields
     */
    public function get_fields() {
        return $this->_postedFormFields;
    }

    /**
     * Function returns errors captured by the validator.
     * 
     * @return array form validation errors
     */
    public function get_errors() {
        return $this->_errors;
    }

    /**
     * Function returns errors encoded as JSON
     * @return string JSON Formated, errors string
     */
    public function get_errors_json() {
        return json_encode($this->_errors);
    }
    
    /**
     *
     * @return array The rules !!
     */
    public function get_rules() {
        return $this->_rules;
    }


//= Rules ==========================================================================================================

    private function check_if_true($field, $value, $rule_value){
        /**
         * usage:
         * 'check_if_true'=> expression, // expression must be TRUE or FALSE
         */
        if (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean (TRUE or FALSE)");
        }
        $err_msg = "Rule's value must be Boolean (TRUE or FALSE)";
        if ($rule_value===TRUE){
            return TRUE;
        } else {
            $this->_errors[$field][] = $err_msg;
            return FALSE;
        }
    }

    private function check_if_true_c($field, $value, $rule_value){
        /**
         * usage:
         * 'check_if_true_c'=> array(
                    'value' => expression, // expression must be TRUE or FALSE
                    'custom_msg'=>'custom error message'
            ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array and must be type of String");
        }
        
        if ($rule_value['value']===TRUE){
            return TRUE;
        } else {
            $this->_errors[$field][] = $rule_value['custom_msg'];
            return FALSE;
        }
    }
    
    private function check_if_false($field, $value, $rule_value){
        /**
         * usage:
         * 'check_if_false'=> expression, // expression must be TRUE or FALSE
         */
        if (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean (TRUE or FALSE)");
        }
        $err_msg = "Rule's value must be Boolean (TRUE or FALSE)";
        if ($rule_value===FALSE){
            return TRUE;
        } else {
            $this->_errors[$field][] = $err_msg;
            return FALSE;
        }
    }

    private function check_if_false_c($field, $value, $rule_value){
        /**
         * usage:
         * 'check_if_false_c'=> array(
                    'value' => expression, // expression must be TRUE or FALSE
                    'custom_msg'=>'custom error message'
            ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array and must be type of String");
        }
        
        if ($rule_value['value']===FALSE){
            return TRUE;
        } else {
            $this->_errors[$field][] = $rule_value['custom_msg'];
            return FALSE;
        }
    }
    
    /**
     * Function checks if string is an email address.
     * 
     * @param string $string string to be checked
     * @return boolean true if string is email. false otherwise
     */
    private function email($field, $value, $rule_value) {
        $default_err_msg = 'Please input a valid email address.';
        $REGEX_EMAIL = '/^[A-Z0-9._%+-]+@[A-Z0-9.-]+.[A-Z]{2,4}$/i';
        if (is_array($value)) {
            foreach ($value as $v) {
                if (!preg_match($REGEX_EMAIL, filter_var((string)$v, FILTER_SANITIZE_EMAIL))) {
                    $this->_errors[$field][] = $default_err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (!preg_match($REGEX_EMAIL, filter_var((string)$value, FILTER_SANITIZE_EMAIL))) {
                $this->_errors[$field][] = $default_err_msg;
                return FALSE;
            }
            return TRUE;
        }
    }

    /**
     * Function returns FALSE if the field contains 
     * anything other than alphabetical characters.
     * 
     * @param type $string
     * @return boolean
     */
    private function alpha($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alpha' => TRUE,
         * ),
         */
        if (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean");
        }
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
            'o','p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 
            'O','P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',);
        $err_msg = 'This field should contain only alphabetical characters [a-z, A-Z]';
        if (is_array($value)) {
            foreach ($value as $v) {
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        }
    }

    private function alpha_c($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alpha_c' => array(
         *          'value' => TRUE,
                    'custom_msg'=>'custom error msg'
         *     ),
         * ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array and must be type of String");
        }
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 
            'o','p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 
            'O','P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',);
        $err_msg = $rule_value['custom_msg'];
        if (is_array($value)) {
            foreach ($value as $v) {
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        }
    }

    /**
     * Function returns FALSE is the field contains
     * anything other than alphanumerical characters,
     * and special characters such as: plus, dash, underscore
     * 
     * @param string $string
     * @return boolean
     */
    private function alphaNum($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alphaNum' => TRUE,
         * ),
         */
        IF (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean");
        }
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o','p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O','P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
        $err_msg = 'This field should contain only alphabetical characters [a-z, A-Z, 0-9]';
        if (is_array($value)) {
            foreach ($value as $v) {
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        }
    }

    private function alphaNum_c($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alphaNum_c' => array(
         *          'value' => TRUE,
                    'custom_msg'=>'custom error msg'
         *     ),
         * ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array and must be type of String");
        }
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o','p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O','P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
        $err_msg = $rule_value['custom_msg'];
        if (is_array($value)) {
            foreach ($value as $v) {
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        }
    }

/**
     * Function checks whether the input
     * holds a numeric value.
     *
     * @param mixed $input the value to check
     * @return  
     */
    private function numeric($field, $value, $rule_value) {
        /**
         * usage:
         * 'numeric'=>TRUE,
         */
        if (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean");
        }
        if (empty($value)){
            $this->_errors[$field][] = "This field should hold a numeric value";
            return FALSE;
        }
        if (is_numeric($value)) {
            return TRUE;
        } else {
            $this->_errors[$field][] = "This field should hold a numeric value";
            return FALSE;
        }
    }
    
    private function numeric_c($field, $value, $rule_value) {
        /**
         * usage:
         * 'numeric'=>array(
         *     'value'=>TRUE,
         *     'custom_msg'=>'custom error msg',
         * ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's value array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's value array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's value array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's value array and must be type of String");
        }
        
        if (empty($value)){
            $this->_errors[$field][] = $rule_value['custom_msg'];
            return FALSE;
        }
        if (is_numeric($value)) {
            return TRUE;
        } else {
            $this->_errors[$field][] = $rule_value['custom_msg'];
            return FALSE;
        }
    }

    private function onlyDigits($field, $value, $rule_value) {
        /**
         * usage:
         * 
         * 'onlyDigits' => true,
         */
        if (!is_bool($rule_value)){
            throw new Exception("Rule's value must be Boolean");
        }
        $err_msg = 'This field must have a numeric value [0-9].';
        if (empty($value)){
            $this->_errors[$field][] = $err_msg;
            return FALSE;
        }
        $digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');

        if (is_array($value)) {
            foreach ($value as $v) {
                $n = $this->sanitize($v);
                $len = strlen($n);
                if ($len < 1) {
                    $this->_errors[$field][] = "All elements of this field must have a numeric value [0-9]";
                    return FALSE;
                }

                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($n, $i, 1), $digits)) {
                        $this->_errors[$field][] = "All elements of this field must have a numeric value [0-9]";
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $n = $this->sanitize($value);
            $len = strlen($n);
            if ($len < 1) {
                $this->_errors[$field][] = $err_msg;
                return FALSE;
            }

            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($n, $i, 1), $digits)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        }
    }
    
    private function onlyDigits_c($field, $value, $rule_value) {
        /**
         * usage:
         * 
         * 'onlyDigits' => array(
         *     'value'=>TRUE,
         *     'custom_msg'=>'custom error msg',
         * ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's value array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's value array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's value array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's value array and must be type of String");
        }
        
        $err_msg = 'This field must have a numeric value [0-9].';
        if (empty($value)){
            $this->_errors[$field][] = $rule_value['custom_msg'];
            return FALSE;
        }
        $digits = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');

        if (is_array($value)) {
            foreach ($value as $v) {
                $n = $this->sanitize($v);
                $len = strlen($n);
                if ($len < 1) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }

                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($n, $i, 1), $digits)) {
                        $this->_errors[$field][] = $rule_value['custom_msg'];
                        return FALSE;
                    }
                }
            }
            return TRUE;
        } else {
            $n = $this->sanitize($value);
            $len = strlen($n);
            if ($len < 1) {
                $this->_errors[$field][] = $rule_value['custom_msg'];
                return FALSE;
            }

            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($n, $i, 1), $digits)) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }
            }
            return TRUE;
        }
    }

    /**
     * Function checks whether the input
     * is longer than a specified minimum length
     * and returns a boolean
     *  
     * @param mixed $input  the string or value to check
     * @param int $length   the minimum length required
     * @return boolean 
     */
    private function min_length($field, $value, $rule_value) {
        /**
         * usage:
         * 
         *  'min_length' => 4, 
         */
        if (!is_int($rule_value)){
            throw new Exception("Rule value must be an Integer");
        }
        if (is_array($value)){
            foreach ($value as $v) {
                if (strlen($this->sanitize($v)) > $rule_value) {
                    $this->_errors[$field][] = "One of this field's element, does not meet the minimum length";
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (strlen($this->sanitize($value)) < $rule_value) {
                $this->_errors[$field][] = "This field does not meet the minimum length";
                return FALSE;
            } else {
                return TRUE;
            }
        }
    }

    private function min_length_c($field, $value, $rule_value) {
        /**
         * usage:
         * 
            'min_length_c' => array(
                'value' => 4,
                'custom_msg'=>'no less than 4 characters'
            ), 
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule value must be an Array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in the Rule's array");
        }
        if(!is_int($rule_value['value'])){
            throw new Exception("[value] key must exists in the Rule's array and must be an Integer");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in the Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in the Rule's array and must be type of String");
        }
        if (is_array($value)){
            foreach ($value as $v) {
                if (strlen($this->sanitize($v)) < $rule_value['value']) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (strlen($this->sanitize($value)) < $rule_value['value']) {
                $this->_errors[$field][] = $rule_value['custom_msg'];
                return FALSE;
            } else {
                return TRUE;
            }
        }
    }
    
    private function max_length($field, $value, $rule_value) {
        /**
         * usage:
         * 
         *  'max_length' => 16, 
         */
        if (!is_int($rule_value)){
            throw new Exception("Rule value must be an Integer");
        }
        if (is_array($value)){
            foreach ($value as $v) {
                if (strlen($this->sanitize($v)) > $rule_value) {
                    $this->_errors[$field][] = "One of this field's element, exceeds the maximum length";
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (strlen($this->sanitize($value)) > $rule_value) {
                $this->_errors[$field][] = "This field exceeds the maximum length";
                return FALSE;
            } else {
                return TRUE;
            }
        }
    }

    private function max_length_c($field, $value, $rule_value) {
        /**
         * usage:
         * 
            'max_length' => array(
                'value' => 16,
                'custom_msg'=>'no more than 16 characters'
            ), 
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule value must be an Array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in the Rule's array");
        }
        if(!is_int($rule_value['value'])){
            throw new Exception("[value] key must exists in the Rule's array and must be an Integer");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in the Rule's array");
        }
        if (!is_string($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in the Rule's array and must be type of String");
        }
        if (is_array($value)){
            foreach ($value as $v) {
                if (strlen($this->sanitize($v)) > $rule_value['value']) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (strlen($this->sanitize($value)) > $rule_value['value']) {
                $this->_errors[$field][] = $rule_value['custom_msg'];
                return FALSE;
            } else {
                return TRUE;
            }
        }
    }
    
    private function alphaNum_noCap_firstLetter_c($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alphaNum_noCap_firstLetter_c' => array(
         *          'value' => TRUE,
                    'custom_msg'=>'custom error msg'
         *     ),
         * ),
         */
        if (!is_array($rule_value)){
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array");
        }
        if (!is_bool($rule_value['value'])){
            throw new Exception("[value] key must exists in Rule's array and must be Boolean");
        }
        if (!isset($rule_value['custom_msg'])){
            throw new Exception("[custom_msg] key must exists in Rule's array");
        }
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_');
        $nums = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_');
        $err_msg = $rule_value['custom_msg'];
        if (is_array($value)) {
            foreach ($value as $v) {
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }

                if (in_array($val[0], $nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }

            if (in_array($val[0], $nums)) {
                $this->_errors[$field][] = $err_msg;
                return FALSE;
            }
            return TRUE;
        }
    }
    
    private function alphaNum_noCap_firstLetter($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *     'alphaNum_noCap_firstLetter' => TRUE,
         * ),
         */
        
        
        $chars_nums = array(
            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_');
        $nums = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '_');
        $err_msg = 'no capital letters, first char must be in [a-z], all other chars must be in [a-z,0-9_] ';
        if (is_array($value)) {
            foreach ($value as $v) {
                if (empty($v)){ continue; }
                $val = $this->sanitize($v);
                $len = strlen($val);
                for ($i = 0; $i < $len; $i++) {
                    if (!in_array(substr($val, $i, 1), $chars_nums)) {
                        $this->_errors[$field][] = $err_msg;
                        return FALSE;
                    }
                }

                if (in_array($val[0], $nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (empty($value)){ return FALSE; }
            $val = $this->sanitize($value);
            $len = strlen($val);
            for ($i = 0; $i < $len; $i++) {
                if (!in_array(substr($val, $i, 1), $chars_nums)) {
                    $this->_errors[$field][] = $err_msg;
                    return FALSE;
                }
            }

            if (in_array($val[0], $nums)) {
                $this->_errors[$field][] = $err_msg;
                return FALSE;
            }
            return TRUE;
        }
    }

    private function not_in($field, $value, $rule_value) {
        /**
         * usage:
         * 'username' => array(
         *       'not_in'=>array(value1, value2, valueN), 
         * ),
         * 
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule's value must be an array");
        }

        if (is_array($value)) {
            foreach ($value as $v) {
                if (in_array($this->sanitize($v), $rule_value)) {
                    $this->_errors[$field][] = 'This value already exists. Must be unique';
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (in_array($this->sanitize($value), $rule_value)) {
                $this->_errors[$field][] = 'This value already exists. Must be unique';
                return FALSE;
            }
            return TRUE;
        }
    }

    private function not_in_c($field, $value, $rule_value) {
        /**
         * usage:
         * 
         * 'username' => array(
         *     'not_in_c'=>array(
         *           'value'=>array(value1, value2, valueN), //must always provide an one dimension array
         *           'custom_msg'=>"custom error msg"
         *      ),
         * 
         * ),
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])) {
            throw new Exception("Value key must exists in Rule's value array");
        }
        if (!is_array($rule_value['value'])) {
            throw new Exception("Value key must exists in Rule's value array and must be an array");
        }
        if (!isset($rule_value['custom_msg'])) {
            throw new Exception("custom_msg key must exists in Rule's value array");
        }
        if (!is_string($rule_value['custom_msg'])) {
            throw new Exception("custom_msg key must exists in Rule's value array and must be type of String");
        }

        if (is_array($value)) {
            foreach ($value as $v) {
                if (in_array($this->sanitize($v), $rule_value)) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (in_array($this->sanitize($value), $rule_value)) {
                $this->_errors[$field][] = $rule_value['custom_msg'];
                return FALSE;
            }
            return TRUE;
        }
    }

    private function in($field, $value, $rule_value) {
        /**
         * 
         *    usage:
         *    'usernames' => array(
         *       'in'=>array(value1, value2, valueN),       // OR
         *    ),
         *
         *    if you provide an array with one single value, it is the same as "EQUALS":
         *
         *    'username' => array(
         *       'in'=>array(value1), // just checks if 'username' is equal to value1
         *    ),
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule's value must be an array");
        }

        if (is_array($value)) {
            foreach ($value as $v) {
                if (!in_array($this->sanitize($v), $rule_value)) {
                    $this->_errors[$field][] = 'This value must exist in the fieldset';
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (!in_array($this->sanitize($value), $rule_value)) {
                $this->_errors[$field][] = "This value must exist in the fieldset.";
                return FALSE;
            }
            return TRUE;
        }
    }

    private function in_c($field, $value, $rule_value) {
        /**
         * usage:
         * 
         * 'username' => array(
         *     'in_c'=>array(
         *           'value'=>array(value1, value2, valueN), //must always provide an one dimension array
         *           'custom_msg'=>"custom error msg"
         *      ),
         * 
         * ),
         * 
         *    if you provide an array with one single value, it is the same as "EQUALS":
         *
         *    'username' => array(
         *       'in_c'=>array(
         *           'value'=>array(value1), // just checks if 'username' is equal to value1
         *           'custom_msg'=>"custom error msg"
         *       ),
         *    ),         
         */
        if (!is_array($rule_value)) {
            throw new Exception("Rule's value must be an array");
        }
        if (!isset($rule_value['value'])) {
            throw new Exception("Value key must exists in Rule's value array");
        }
        if (!is_array($rule_value['value'])) {
            throw new Exception("Value key must exists in Rule's value array and must be an array");
        }
        if (!isset($rule_value['custom_msg'])) {
            throw new Exception("custom_msg key must exists in Rule's value array");
        }
        if (!is_string($rule_value['custom_msg'])) {
            throw new Exception("custom_msg key must exists in Rule's value array and must be type of String");
        }

        if (is_array($value)) {
            foreach ($value as $v) {
                if (!in_array($this->sanitize($v), $rule_value['value'])) {
                    $this->_errors[$field][] = $rule_value['custom_msg'];
                    return FALSE;
                }
            }
            return TRUE;
        } else {
            if (!in_array($this->sanitize($value), $rule_value['value'])) {
                $this->_errors[$field][] = $rule_value['custom_msg'];
                return FALSE;
            }
            return TRUE;
        }
    }

    /**
     * Function calls filter_var_array() to filter form data
     * 
     * @param type $sanitized_data  data to be sanitized
     * @return mixed    sanitized form data
     */
    private function sanitize($v) {
        $sanitized_data = filter_var((string)$v, FILTER_SANITIZE_STRING);

        // Return the sanitized datas
        return $sanitized_data;
    }
}

?>
